package projet_v1;

import java.util.ArrayList;
import java.util.List;

import projet_v1.simulation.Field;
import projet_v1.simulation.Location;

/**
 * Parent Character class.
 * 
 * @author Antoine Lavail
 * 
 */

public abstract class Character implements Actor {

    // represents the health (once down to 0, this character will be destroyed)
    protected int healthPoints;
    // The breed of the character
    private Breed breed;
    // Who has beaten at last
    protected Breed lastAttackedBy;
    // The character's position.
    protected Location location;
    // The field occupied.
    protected Field field;
    // The field occupied.
    protected int speed;

    /**
     * Constructor of Character class.
     * 
     * @param healthPoints
     *            initial HP
     * @param b
     *            : The wanted breed
     */
    public Character(int healthPoints, Breed b) {
        if (healthPoints < 0)
            throw new IllegalArgumentException(
                    "Negative healthPoints passed to the Character constructor.");
        if (b == null)
            throw new NullPointerException(
                    "null breed passed in the Character constructor.");
        this.healthPoints = healthPoints;
        this.location = null;
        this.field = null;
        this.lastAttackedBy = null;
        this.breed = b;
    }

    /**
     * Another constructor of Character class.
     * 
     * @param healthPoints
     *            : initial HP.
     * @param breed
     *            : the wanted breed.
     * @param f
     *            : the current field.
     * @param l
     *            : the wanted location for the character.
     * @param speed
     *            : the speed of the character.
     */
    public Character(int healthPoints, Breed breed, Field f, Location l,
            int speed) {
        if (healthPoints < 0)
            throw new IllegalArgumentException(
                    "Negative healthPoints passed to the Character constructor.");
        if (breed == null)
            throw new NullPointerException(
                    "null breed passed in the Character constructor.");
        if (f == null)
            throw new NullPointerException(
                    "null field passed in the Character constructor.");
        if (l == null)
            throw new NullPointerException(
                    "null location passed in the Character constructor.");
        if (speed < 0)
            throw new IllegalArgumentException(
            "Negative speed passed in the Character constructor.");

        this.healthPoints = healthPoints;
        this.breed = breed;
        this.field = f;
        this.location = l;
        this.lastAttackedBy = null;
        this.speed = speed;
    }

    // Accessors

    public int getHealthPoints() {
        return healthPoints;
    }

    public void setHealthPoints(int i) {
        if (i < 0)
            throw new IllegalArgumentException(
                    "Negative healthPoints passed in the setHealthPoints method.");
        healthPoints = i;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location l) {
        if (l == null)
            throw new NullPointerException(
                    "null location passed in the setLocation method.");
        location = l;
    }

    public Breed getLastAttackedBy() {
        return lastAttackedBy;
    }

    public void setLastAttackedBy(Breed b) {
        lastAttackedBy = b;
    }

    public Breed getBreed() {
        return breed;
    }

    public int getSpeed() {
        return speed;
    }

    /**
     * Check whether the character is alive or not.
     * 
     * @return True if the character is still alive.
     */
    public boolean isAlive() {
        return this.getHealthPoints() > 0;
    }

    /**
     * Decrease the number of HP by a certain amount. HP cannot go below 0.
     * 
     * @param reduction
     *            number of HP to reduce
     */
    public void reduceHealthPoints(int reduction) {
        healthPoints = healthPoints - reduction;
        if (healthPoints < 0) {
            healthPoints = 0;
        }
    }

    /**
     * Output a character's saying to the screen
     * 
     * @param str
     *            what the character says
     */
    public void say(String str) {
        System.out.println("Character says: " + str);
    }

    /**
     * Method triggered when the character described by the current object meets
     * another character, and does something to him (for example, attack).
     * 
     * @param c
     *            the other character that this character meets
     */
    public void encounterCharacter(Character c) {
        // Default action: do nothing
    }

    /**
     * The actions of a character at the end of the turn.
     */
    public void endOfTurn() {
        // Do nothing.
    }

    /**
     * The character can be aware of the other characters around him
     * 
     * @param updatedField
     *            the field which contains all the characters
     * @return a list containing the characters around him
     */
    protected List<Character> aroundCharacter(Field updatedField) {
        if (updatedField == null)
            throw new IllegalArgumentException(
                    "null updatedField passed in the aroundCharacter methohd.");

        List<Character> l = new ArrayList<Character>();
        List<Location> adjLoc = updatedField.adjacentLocations(location, 1);
        for (Location location : adjLoc) {
            Character c = updatedField.getCharacterAt(location);
            if ((c != null) && ((Character) c).isAlive()) {
                l.add((Character) c);
            }
        }
        return l;
    }

    /**
     * Make this character act ­ that is: make it do whatever it wants/needs to
     * do.
     * 
     * @param currentField
     *            The field currently occupied.
     * @param updatedField
     *            The field to transfer to.
     * @param newActors
     *            A list to add newly born character to.
     */
    public abstract void act(Field currentField, Field updatedField,
            List<Actor> newActors);
}
